/**
 * 
 */
package ua.lviv.franko.solvers.stressanalysis.twoDimention.fem;

import java.util.ArrayList;
import java.util.List;

import ua.lviv.franko.integration.IFEMFunction2D;
import ua.lviv.franko.integration.IFunction2D;
import ua.lviv.franko.solvers.stressanalysis.twoDimention.ElementMatrixForStress;
import ua.lviv.franko.solvers.stressanalysis.twoDimention.savestructures.IElement2DForStress;

/**
 * @author marko
 * 
 */
public abstract class AbstractStressElement implements IElement2DForStress {

	// private static final int UNKWON_VALUES = 3;
	// private static final int SMALL_MATRIX_SIZE = 2;

	// private double[][] D;
	protected List<IFEMFunction2D>	baseFunctions;

	private double[][]				kMatrix;

	protected double				k1;
	protected double				k2;
	protected double				k3;
	protected double				E;
	protected double				v;
	protected IFunction2D			f1Function;
	protected IFunction2D			f2Function;

	/**
	 * 
	 * @param k1
	 *            - d11 and d22 elements of D matrix
	 * @param k2
	 *            - d12 and d21 elements of D matrix
	 * @param k3
	 *            - d33 element of D matrix
	 * @param fFunction
	 *            - function of inside forces
	 */
	public AbstractStressElement(double k1, double k2, double k3, IFunction2D f1Function, IFunction2D f2Function) {
		this.k1 = k1;
		this.k2 = k2;
		this.k3 = k3;
		this.f1Function = f1Function;
		this.f2Function = f2Function;
		this.baseFunctions = new ArrayList<IFEMFunction2D>();
	}

	public AbstractStressElement(double e, double v, IFunction2D f1Function, IFunction2D f2Function) {
		super();
		E = e;
		this.v = v;
		this.f1Function = f1Function;
		this.f2Function = f2Function;
		this.k1 = e / (1 - v * v);
		this.k2 = e * v / (1 - v * v);
		this.k3 = e / (1 - v * v) * ((1 - v) / 2.0);
		this.baseFunctions = new ArrayList<IFEMFunction2D>();
	}

	public AbstractStressElement(double k1, double k2, double k3, double e, double v, IFunction2D f1Function, IFunction2D f2Function) {
		super();
		this.k1 = k1;
		this.k2 = k2;
		this.k3 = k3;
		E = e;
		this.v = v;
		this.f1Function = f1Function;
		this.f2Function = f2Function;
		this.baseFunctions = new ArrayList<IFEMFunction2D>();
	}

	public ElementMatrixForStress generateAll() {
		ElementMatrixForStress data = new ElementMatrixForStress(this.getNodesNumbers());
		int N = baseFunctions.size();
		for (int i = 0; i < N; i++) {
			ElementMatrixForStress tmp = this.calculateSmallData(i, i);

			data.vector[2 * i] = tmp.vector[0];
			data.vector[2 * i + 1] = tmp.vector[1];

			for (int j = 0; j < N; j++) {
				tmp = this.calculateSmallData(i, j);

				data.matrix[i * 2][j * 2] = tmp.matrix[0][0];
				data.matrix[i * 2][j * 2 + 1] = tmp.matrix[0][1];
				data.matrix[i * 2 + 1][j * 2] = tmp.matrix[1][0];
				data.matrix[i * 2 + 1][j * 2 + 1] = tmp.matrix[1][1];
			}

		}
		return data;
	}

	public void generateMatrix() {
		int N = baseFunctions.size();
		kMatrix = new double[N * 2][N * 2];
		// as K is symmetric we calculate only upper part
		for (int i = 0; i < N; i++) {

			double[][] smallMatrix = this.calculateSmallMatrix(i, i);

			kMatrix[i * 2][i * 2] = smallMatrix[0][0];
			kMatrix[i * 2][i * 2 + 1] = smallMatrix[0][1];
			kMatrix[i * 2 + 1][i * 2] = smallMatrix[1][0];
			kMatrix[i * 2 + 1][i * 2 + 1] = smallMatrix[1][1];

			for (int j = i + 1; j < N; j++) {
				smallMatrix = this.calculateSmallMatrix(i, j);

				// upper part
				kMatrix[i * 2][j * 2] = smallMatrix[0][0];
				kMatrix[i * 2][j * 2 + 1] = smallMatrix[0][1];
				kMatrix[i * 2 + 1][j * 2] = smallMatrix[1][0];
				kMatrix[i * 2 + 1][j * 2 + 1] = smallMatrix[1][1];

				// lower part
				kMatrix[j * 2][i * 2] = smallMatrix[0][0];
				kMatrix[j * 2][i * 2 + 1] = smallMatrix[0][1];
				kMatrix[j * 2 + 1][i * 2] = smallMatrix[1][0];
				kMatrix[j * 2 + 1][i * 2 + 1] = smallMatrix[1][1];
			}
		}
	}

	public double[] generateVector() {
		int N = baseFunctions.size();
		double[] fVector = new double[N * 2];
		for (int i = 0; i < N; i++) {
			double[] smallVector = this.calculateSmallVector(i);
			fVector[i * 2] = smallVector[0];
			fVector[i * 2 + 1] = smallVector[1];
		}
		return fVector;
	}

	@Override
	public List<IFEMFunction2D> getBaseFunctions() {
		return baseFunctions;
	}

	protected abstract ElementMatrixForStress calculateSmallData(int i, int j);

	protected abstract double[][] calculateSmallMatrix(int i, int j);

	protected abstract double[] calculateSmallVector(int i);

	public abstract List<Integer> getNodesNumbers();

	public double getMuCoef() {
		return E / (2.0 * (1 + v));
	}

	public double getLjambdaCoef() {
		return E * v / ((1 + v) * (1 - 2.0 * v));
	}

	public double getK1() {
		return k1;
	}

	public double getK2() {
		return k2;
	}

	public double getK3() {
		return k3;
	}

	public IFunction2D getF1Function() {
		return f1Function;
	}

	public IFunction2D getF2Function() {
		return f2Function;
	}
}
